package cloud.lysky.java.on.concurrent.lock.stampedlock;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.StampedLock;

/**
 * @author luoly
 * @describe StampedLock在缓存中的应用
 * @date 2023-04-30 21:55
 **/
// 将用户id与用户名数据保存在 共享变量 idMap 中，并且提供 put 方法添加数据、get 方法获取数
//据、以及 getIfNotExist 先从 map 中获取数据，若没有则模拟从数据库查询数据并放到 map 中。
public class CacheStampedLock {
    // 保存数据的变量
    private final Map<Integer, String> idMap = new HashMap<>();
    private final StampedLock lock = new StampedLock();

    // 添加数据，独占模式
    public void put(Integer key, String value) {
        long stamp = lock.writeLock();
        try {
            idMap.put(key, value);
        } finally {
            lock.unlockWrite(stamp);
        }
    }
    // 读取数据，只读模式
    public String get(Integer key) {
        // 1.尝试通过乐观读模式读取数据，非阻塞
        long stamp = lock.tryOptimisticRead();
        // 2.读取数据到当前线程栈
        String currentValue = idMap.get(key);
        // 3.检查乐观读锁后是否有其他写锁发生
        if (!lock.validate(stamp)) {
            // 4.升级为悲观读锁，并重新读取数据到当前线程局部变量
            stamp = lock.readLock();
            try {
                // 5.重新读取数据到当前线程栈
                currentValue = idMap.get(key);
            } finally {
                // 6.释放悲观读锁
                lock.unlockRead(stamp);
            }
        }
        return currentValue;
    }
    // 如果数据不存在，则从数据库查询数据并添加到 map 中(锁升级运用)
    public String getIfNotExist(Integer key){
        // 获取读锁，也可以直接调用get方法使用乐观读
        long stamp = lock.readLock();
        String currentValue = idMap.get(key);
        // 缓存为空则尝试上写锁从数据库读取数据并写入缓存
        try {
            while (Objects.isNull(currentValue)){
                // 尝试升级写锁
                long writeLock = lock.tryConvertToWriteLock(stamp);
                // 不为0 升级写锁成功
                if (writeLock != 0L) {
                    // 更新戳记
                    stamp = writeLock;
                    // 执行写操作
                    // 模拟从数据库读取数据
                    currentValue = "lyskymoon";
                    idMap.put(key, currentValue);
                    break;
                } else {
                    // 读锁升级写锁失败，释放读锁，显式获取独占写锁，然后循环重试
                    lock.unlockRead(stamp);
                    stamp = lock.writeLock();
                }
            }
        } finally {
            // 释放最后加的锁
            lock.unlock(stamp);
        }
        return currentValue;
    }

    public static void main(String[] args) {
        CacheStampedLock cache = new CacheStampedLock();
        cache.put(1, "hello");
        System.out.println(cache.get(1));
        System.out.println(cache.getIfNotExist(2));
        System.out.println(cache.get(2));
    }
}
